// @HEADER
//*********************************************************************//
//  SiYuan: A numerical PDE solver                                     //
//  Copyright (2022) YUAN Xi                                           //
//  This Software is released under the BSD 2-Clause license detailed  //
//  in the file "LICENSE" in the top-level SiYuan directory            //
//*********************************************************************//
// @HEADER

#ifndef __XYZ_VALFUNCTOR_H
#define __XYZ_VALFUNCTOR_H

#include <vector>
#include <valarray>
#include <cmath>
#include <algorithm>
#include <string>
#include <cassert>
#include <complex>

#include <Teuchos_ParameterList.hpp>
#include "Teuchos_Array.hpp"

#include "valtree.hpp"

namespace XYZLib
{

template< typename T, typename... Args >
class valfunctor
{
    typedef std::valarray<T>   evalT;

    public:
        virtual bool isConstant()=0;
        virtual unsigned int size() const =0;
        virtual std::string name() {return m_name;}
        virtual evalT fetch(const Args... args) const=0;
        virtual evalT fetch(const std::initializer_list<T>& args) const=0;

    protected:
        std::string m_name;
};

template< typename T, typename... Args >
class constant: public valfunctor<T, Args...>
{
    typedef std::valarray<T>   evalT;

    public:
        bool isConstant() {return true;}
        unsigned int size() const {return m_size;}
        evalT fetch(const Args... args) const {return m_constant;}
        evalT fetch(const std::initializer_list<T>& args) const {return m_constant;}

    public:
        constant(const Teuchos::ParameterList& params) {
            this->m_name = params.name();
            const auto myArray = params.get<Teuchos::Array<T> >("Value");
            m_size = myArray.size();
            m_constant.resize(m_size);
            for (unsigned int i=0; i < m_size; i++) {
                m_constant[i] = myArray[i];
            }
        };

    private:
        evalT m_constant;
        unsigned int m_size;
};

template< typename T, typename... Args>
class tablefunctor: public valfunctor<T, Args...>
{
    typedef std::valarray<T>   evalT;

    public:
        bool isConstant() {return false;}
    //    evalT fetch(Args... args) {return m_table.GetValue(args);}

    private:
        valtree<T> m_table;
};


/* General variable with type std::valarray<T> */
template< typename T=double >
class variable
{
    typedef valfunctor<T> funcType;
    typedef std::valarray<T>   evalT;

 	private:
		std::shared_ptr< funcType > _val;
        unsigned int _size;
        bool _isConstant;

	public:
        variable() {};
        // only Constant avalilable currently
		variable(const Teuchos::ParameterList& params){
            auto params_type = params.get<std::string>("Type");
            if( params_type=="Constant" ) { 
               _val = std::make_shared< constant<T> >(params);
            } else {
                std::cerr << "Only Constant type avalilable currently!" << std::endl;
                exit(1);
            }
            _size = _val->size();
            _isConstant = _val->isConstant();
         };

        evalT fetch(const std::initializer_list<T>& args) const {return _val->fetch(args);}
        template <typename... Args>
        evalT fetch(const Args... args) const {return _val->fetch(args...);}

        unsigned int size() const {return _size;}
        bool isConstant() const {return _isConstant;}
   };


}

#endif
